Skip to content

Add whatprovides resolution to verify-install plugin#4804

Open
vaibhavdaren wants to merge 2 commits intomainfrom
vaibhav-discover-only-providers-whatprovides
Open

Add whatprovides resolution to verify-install plugin#4804
vaibhavdaren wants to merge 2 commits intomainfrom
vaibhav-discover-only-providers-whatprovides

Conversation

@vaibhavdaren
Copy link
Copy Markdown
Contributor

@vaibhavdaren vaibhavdaren commented Apr 17, 2026

Use whatprovides lookup for file-based dependencies

Allow resolving packages from file paths (e.g. /usr/bin/make)
by leveraging DNF provides/whatprovides instead of requiring
explicit package names.

@vaibhavdaren vaibhavdaren self-assigned this Apr 17, 2026
@vaibhavdaren vaibhavdaren moved this to implement in planning Apr 17, 2026
@vaibhavdaren vaibhavdaren added ci | full test Pull request is ready for the full test execution plugin | artifact Related to the `prepare/artifact` plugin. labels Apr 17, 2026
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements the resolution of virtual RPM provides to actual package names using 'rpm -q --whatprovides' during artifact preparation and installation verification. In 'verify_installation.py', replace the dictionary comprehension for 'verify_map' with a loop using 'setdefault' and 'tmt.utils.uniq' to prevent overwriting entries when multiple virtual provides resolve to the same package name.

Comment thread tmt/steps/prepare/verify_installation.py Outdated
@vaibhavdaren vaibhavdaren force-pushed the vaibhav-discover-only-providers-whatprovides branch from 725fbcf to 1d6cf0d Compare April 17, 2026 16:05
Base automatically changed from vaibhav-discover-only-providers to main April 17, 2026 16:32
@vaibhavdaren vaibhavdaren force-pushed the vaibhav-discover-only-providers-whatprovides branch 5 times, most recently from 9914613 to d189b00 Compare April 20, 2026 10:32
@psss psss added this to the 1.72 milestone Apr 20, 2026
Allow resolving packages from file paths (e.g. /usr/bin/make)
by leveraging DNF provides/whatprovides instead of requiring
explicit package names.
@vaibhavdaren vaibhavdaren force-pushed the vaibhav-discover-only-providers-whatprovides branch 5 times, most recently from d1de079 to e08283d Compare April 21, 2026 00:32
@vaibhavdaren vaibhavdaren moved this from implement to review in planning Apr 21, 2026
@vaibhavdaren vaibhavdaren marked this pull request as ready for review April 21, 2026 00:37
@psss psss changed the title add whatprovides resolution to verify-install plugin Add whatprovides resolution to verify-install plugin Apr 21, 2026
@vaibhavdaren
Copy link
Copy Markdown
Contributor Author

The test failures are unrelated to the change. Flaky Test : /tests/execute/restart/with-reboot 🔎

/packit retest-failed

The script must emit one line per capability in the same order as the input.
Each line is either a valid NEVRA string for a found capability, or an error
message (e.g. ``no package provides <cap>``) for one that is not provided by
any installed package.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The capabilities name is neither generic enough, nor is it correct in the dnf context. For dnf context, provides could be a better fit for what this is.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed to provides.
context: for choosing capability. We already have a provider.

Comment thread tmt/package_managers/__init__.py Outdated
Comment on lines +412 to +413
:param capabilities: Capabilities to resolve — package names, file paths, or
virtual provides (e.g. ``make``, ``/usr/bin/make``, ``pkgconfig(openssl)``).
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the em-dash. Personally, if one uses em-dash, it feels like they did not consider the wording at all.

Comment thread tmt/package_managers/__init__.py Outdated

:param capabilities: Capabilities to resolve — package names, file paths, or
virtual provides (e.g. ``make``, ``/usr/bin/make``, ``pkgconfig(openssl)``).
:returns: A shell script whose stdout contains one NEVRA line per capability.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, too technical. The whose stdout is not relevant and too literal. See the other docstrings

Comment thread tmt/package_managers/__init__.py Outdated
:param capabilities: Capabilities to resolve — package names, file paths, or
virtual provides (e.g. ``make``, ``/usr/bin/make``, ``pkgconfig(openssl)``).
:returns: A shell script whose stdout contains one NEVRA line per capability.
:raises NotImplementedError: If the package manager does not support this query.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the raises just below here. Be consistent.

Comment thread tmt/package_managers/__init__.py Outdated
result[package] = parts[1] if len(parts) == 2 else SpecialPackageOrigin.UNKNOWN
return result

def resolve_capabilities(self, capabilities: Iterable[str]) -> 'dict[str, Optional[Version]]':
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incorrect quotations of the return type

Comment thread tmt/package_managers/__init__.py Outdated

def resolve_capabilities(self, capabilities: Iterable[str]) -> 'dict[str, Optional[Version]]':
"""
Map each capability to the :py:class:`Version` of the installed package providing it.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Map each capability to the :py:class:`Version` of the installed package providing it.
Map each capability to the :py:class:`Version` of the packages providing it.
  • The package is not installed when you need to call this
  • Multiple packages can provide the same provides, particularly if the user did not add relevant constraints

Comment thread tmt/package_managers/__init__.py Outdated
Comment on lines +571 to +575
try:
output = self.guest.execute(script)
stdout = output.stdout
except tmt.utils.RunError as exc:
stdout = exc.stdout
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just wrong isn't it? Can't figure out what you are trying to catch here.

Comment thread tmt/package_managers/__init__.py Outdated
except tmt.utils.RunError as exc:
stdout = exc.stdout

result: dict[str, Optional[Version]] = dict.fromkeys(caps, None)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this at the top and you can avoid the intermediate caps. I commented about this very recently didn't I?

Comment thread tmt/package_managers/__init__.py Outdated
Comment on lines +579 to +580
with contextlib.suppress(ValueError):
result[cap] = RpmVersion.from_nevra(line.strip())
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No contextlib.suppress. There is a special case being handled, then document it properly

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry - just seeing this now, but prolly made a duplicate comment: #4804 (comment)

Comment thread tmt/package_managers/dnf.py Outdated
Comment on lines +212 to +214
def resolve_capabilities(self, *capabilities: str) -> ShellScript:
# Reuse the existing rpm --whatprovides script; output is one NEVRA per line,
return self._construct_presence_script(*[Package(c) for c in capabilities])
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a good approach, see the other comments. The repoquery might give you more flexibility on the format and allow you to get more information to process further like the repoid.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We now , run repoquery for each of the packages in a script. Which is actually easier to parse. Addressed in 0fb9b50.

@LecrisUT LecrisUT self-assigned this Apr 21, 2026
Comment thread tmt/package_managers/__init__.py Outdated

result: dict[str, Optional[Version]] = dict.fromkeys(caps, None)
for cap, line in zip(caps, (stdout or '').splitlines()):
with contextlib.suppress(ValueError):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This supresses parsing errors (and others too) - ambigous. Suggest adding a debug() log here:

for cap, line in zip(caps, (stdout or '').splitlines()):
    try:
        result[cap] = RpmVersion.from_nevra(line.strip())
    except ValueError:
        self.debug(f"Could not resolve capability '{cap}': {line.strip()}")

@vaibhavdaren vaibhavdaren requested a review from psss April 21, 2026 17:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci | full test Pull request is ready for the full test execution plugin | artifact Related to the `prepare/artifact` plugin.

Projects

Status: review

Development

Successfully merging this pull request may close these issues.

4 participants